<template>
    <div class="hls-video">
        <video ref="videoRef" :controls="controls" @loadeddata="log" @loadedmetadata="log" @canplay="log" @canplaythrough="log" @play="log"
               @playing="log" @waiting="log" @seeking="log" @seeked="log" @timeupdate="log" @pause="log" @ended="log"
               @ratechange="log" @durationchange="log" @volumechange="log" @suspend="log" @emptied="log" @stalled="log"
               @error="log" playsinline :muted="muted"></video>
        <div class="absolute top-0 left-0 right-0 bottom-0 flex justify-center items-center pointer-events-none"
             v-loading="!state.error && state.isLoading" element-loading-background="#0000">
            <p v-if="videoRef?.ended" class="error-panel shadow-lg shadow-[#0003] text-white">视频已结束</p>
        </div>
        <div class="absolute left-2 top-2 max-w-[61%]">
            <transition name="slide-fade-left">
                <div v-show="state.error" class="flex error-panel justify-between shadow-lg shadow-[#0003]" row wrap inline>
                    <span class="text-white">{{ state.error && state.error.message }}</span>
                    <a class="ml-4 text-[#01C2FF] hover:text-gray-300" @click.stop="play">重试</a>
                </div>
            </transition>
        </div>
        <div v-if="title" class="title">{{ title }}</div>
    </div>
</template>

<script setup lang="ts">
import { computed, onUnmounted, reactive, ref } from 'vue';
import HLS, { Events, ErrorTypes } from "hls.js";

const props = defineProps<{
    src?: string,
    title?: string,
    controls?: boolean,
    muted?: boolean
}>()

const srcWithoutProtocol = computed(() => {
    return props.src?.replace(/^https?:/,'')
})

const videoRef = ref<HTMLVideoElement>()
let hls: HLS | undefined
const state = reactive({
    isLoading: false,
    error: null as MediaError | null
})

function log(e: Event) {
    if (e.type == 'timeupdate') {
        return;
    }
    switch (e.type) {
        case 'canplaythrough':
        case 'playing':
        case 'ended':
            state.isLoading = false;
            break
        case 'waiting':
            state.isLoading = true;
            break;
        case 'error':
            state.error = (e as ErrorEvent).error;
            console.log(state.error)
            break;
        default:
            state.error = null
    }

}

function play() {
    console.log('play')
    hls?.destroy()
    hls = new HLS()
    hls.attachMedia(videoRef.value!)
    hls.on(Events.ERROR, function (_, data) {
        if (data.fatal) {
            videoRef.value!.dispatchEvent(new ErrorEvent('error', {
                error: data.error ?? data.err ?? new Error(data.type),
                message: data.type
            }))
            switch (data.type) {
                case ErrorTypes.NETWORK_ERROR:
                    console.log('fatal network error encountered, try to recover');
                    // hls?.startLoad();
                    break;
                case ErrorTypes.MEDIA_ERROR:
                    console.log('fatal media error encountered, try to recover');
                    hls?.recoverMediaError();
                    break;
                default:
                    console.error(data)
                    hls?.destroy();
                    break;
            }
        }
    });
    hls.once(Events.MEDIA_ATTACHED, () => {
        if (!srcWithoutProtocol.value) { return }
        hls?.loadSource(srcWithoutProtocol.value)
        hls?.once(Events.MANIFEST_PARSED, () => {
            videoRef.value!.play()
        })
    })

}

function stop() {
    console.log('stop')
    videoRef.value?.pause()
    hls?.destroy()
}

onUnmounted(() => {
    videoRef.value?.pause()
    hls?.destroy()
})

defineExpose({
    play, stop
})

</script>

<style scoped>
.hls-video {
    position: relative;
    background-color: #0004;
    background-image: url(./assets/image.png);
    background-size: 100% 100%;
}

video {
    height: 100%;
    width: 100%;
    object-fit: fill;
}

.title {
    padding-left: 16px;
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 32px;
    background: rgba(0, 0, 0, 0.6);
    font-size: 16px;
    line-height: 32px;
    /* identical to box height, or 125% */

    font-feature-settings: 'tnum' on, 'lnum' on;

    color: #E0F7FF;

    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.error-panel {
    padding: 16px;
    border-radius: 4px;
    background: #222;
}

:deep(.el-loading-spinner .path) {
    stroke: #E0F7FF;
}
</style>